Object Pool Pattern
筆者もお気に入りのデザインパターン
オブジェクトを予め、決まった数だけインスタンス化しておき、
enable, disableで生成、消去の代わりとする。
メモリ領域の節約
生成、消去のコストをなくす
code
code: .cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolItem
{
public GameObject prefab;
public int amount;
}
public class Pool : MonoBehaviour
{
public static Pool singleton;
public List<PoolItem> items;
public List<GameObject> pooledItems;
public GameObject GetItem(string tag)
{
for (int i = 0; i < pooledItems.Count; i++)
{
if (!pooledItemsi.activeInHierarchy && pooledItemsi.tag == tag) {
}
}
return null;
}
void Awake()
{
singleton = this;
}
void Start()
{
pooledItems = new List<GameObject>();
foreach (PoolItem item in items)
{
for (int i = 0; i < item.amount; i++)
{
GameObject obj = Instantiate(item.prefab);
obj.SetActive(false);
pooledItems.Add(obj);
}
}
}
}
[System.Serializable] class ... でクラス定義すると、
Seliarize Field としてそのクラスをプロパティに持つ時
インスペクタ上でセットできる。
仕組み
アイテムリスト(種類と数)と、pooledItem(種類関係ない、GameObjectのリスト)
Awakeで、public static singletonに自身をセット
シングルトンパターンだが、ゲームオブジェクトとしてヒエラルキーに配置するならばインスタンス化の処理は書かなくていい
GetItem()
gameObject.activeInHierarcy : enable かどうか
gameObject.tag : タグ文字列
プールされているオブジェクトのうち、非アクティブかつ検索タグが同じものを返す
なかったらnullを返す
一応、入れる想定なのは
Bullet
Asteroid
弾丸の発射
Instantiate()の代わりに
Pool.singleton.Get(tag)して、null でなければ
発生位置にtransform.positionをセット
gameObject.SetActive(true)
Destroy()の代わりに
gameObject.SetActive(false)
Expandable Pool
もしプールされている限界以上のオブジェクトが欲しくなった場合、
足りない場合に1足す事もできる。
code: .cs
public class PoolItem
{
public GameObject prefab;
public int amount;
public bool expandable;
}
public class Pool : MonoBehaviour
{
public GameObject GetItem(string tag)
{
for (int i = 0; i < pooledItems.Count; i++)
{
if (!pooledItemsi.activeInHierarchy && pooledItemsi.tag == tag) {
}
}
foreach (PoolItem item in items)
{
if (item.prefab.tag == tag && item.expandable)
{
GameObject obj = Instantiate(item.prefab);
obj.SetActive(false);
pooledItems.Add(obj);
return obj;
}
}
return null
}
上の例では、プールされたオブジェクトで理容なものがなかったら、
List<GameObject> pooledListに1つ追加する。
(InActive状態で)
で作ったものをそのまま返す。
GetItem(tag)のtag名がマッチしない場合はnullを返す
HPバーを作る
UIElement.Sliderを使う
UI > Slider からキャンバスに追加
Slider
Background
color を赤に
Fill Area
Fill
color を緑に
あとは、slider.valueを変更すればOK
以下のプロパティもインスペクタから設定しておく
slider.max
slider.min
UI要素の位置を変える
2D ビューの場合も、位置の設定にはVector3を使うが、
スクリーン座標とワールド座標は単位が異なる。
よって、Cameraオブジェクトが持っているインスタンスメソッドを使って
スクリーン座標に変換してからセットする必要がある
code: .cs
Vector3 drivePosOnScreen = Camera.main.WorldToScreenPoint(drive.transform.position) + new Vector3(0, -130, 0);
healthbar.transform.position = screenPos;
Sprite のワールド座標 -> メインカメラのスクリーン座標
に変換した位置のy方向-130に
ヘルスバーを移動。
スクリーン座標は、ピクセル座標?
OnCollisionEnter2D + HPの減少
衝突した相手のタグがasteroidだったら、healthBarの数値を-30する
Slider.valueだけで管理するなら、その方が簡単だが、
より堅牢な仕組みにするなら State と UI 表示プロセスは分けたほうがいい
code: .cs
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag == "asteroid")
{
other.gameObject.SetActive(false);
healthBar.value -= 30;
if (healthBar.value <= 0)
{
Instantiate(exprosion, this.transform.position, Quaternion.identity);
Destroy(healthBar.gameObject, 0.1f);
Destroy(this.gameObject, 0.1f);
}
}
}
HPがゼロになったら、自機とSlider をDestory
もしくは、SetActive(false)でもいい
爆破エフェクト
FX Explsion Packをインポートして、
船のHPがゼロになったときに
爆破エフェクトのPrefabをインスタンス化すると良い